import tools from "../tools"

export default class KeyList extends Element {
    constructor() {
        super()
        /**
         * 监听搜索事件
         */
        document.on("search", (evt) => {
            this.searchKey = evt.data.key
            this.fetchKeyList(evt.data.key)
        })
        /**
         * 监听顶部刷新事件
         */
        document.on("refresh", () => {
            this.fetchKeyList()
            this.vlist && this.vlist.navigate("start")
        })
        /**
         * 监听key列表刷新事件
         */
        document.on("refresh-key-list", () => {
            this.fetchKeyList()
        })
        /**
         * 监听数据库改变事件
         */
        document.on("db-change", () => {
            this.searchKey = null
            this.fetchKeyList()
            this.vlist && this.vlist.navigate("start")
        })

    }

    componentDidMount() {
        /**
         * 获取key列表
         */
        this.fetchKeyList()
    }

    keyList = []

    // scroll down
    appendElements(index, n) {
        if (index === undefined) index = 0
        let elements = []
        for (let i = 0; i < n; ++i, ++index) {
            if (index >= this.totalItems()) break
            let item = this.itemAt(index)
            elements.push(this.renderItem(item, index))
        }
        this.append(elements)
        return {moreafter: (this.totalItems() - index)} // return estimated number of items below this chunk
    }

    // scroll up
    prependElements(index, n) {

        if (index === undefined) index = this.totalItems() - 1
        let elements = []
        for (let i = 0; i < n; ++i, --index) {
            if (index < 0) break
            let item = this.itemAt(index)
            elements.push(this.renderItem(item, index))
        }
        elements.reverse()
        this.prepend(elements)
        return {morebefore: (index < 0 ? 0 : index + 1)} // return estimated number of items above this chunk
    }

    // scroll to
    replaceElements(index, n) {
        let elements = []
        let start = index
        for (let i = 0; i < n; ++i, ++index) {
            if (index >= this.totalItems()) break
            let item = this.itemAt(index)
            elements.push(this.renderItem(item, index))
        }
        this.patch(elements)
        return {
            morebefore: start <= 0 ? 0 : start,
            moreafter: this.totalItems() - index
        }
    }

    // sciter virtual-list content load function
    oncontentrequired(evt) {
        let {length, start, where} = evt.data
        if (where > 0) evt.data = this.appendElements(start, length)  // scrolling down, need to append more elements
        else if (where < 0) evt.data = this.prependElements(start, length) // scrolling up, need to prepend more elements
        else evt.data = this.replaceElements(start, length) // scrolling to index
        return true
    }

    // sciter virtual-lis function
    itemAt(at) {
        return this.keyList[at]
    }

    // sciter virtual-lis function
    totalItems() {
        return this.keyList.length || 0
    }

    // sciter virtual-lis function
    indexOf(item) {
        return this.keyList.indexOf(item)
    }

    /**
     * 点击事件
     * @param key {string} redis key
     */
    keyClick(key) {
        this.post(new Event("key-change", {bubbles: true, data: {key}}))
        this.componentUpdate()
    }

    /**
     * 命名空间折叠
     * @param evt {Event} 事件
     * @param ele {Element} sciter element 当前元素
     * @param type {string} 类型 是否是key或者命名空间
     * @param key {string} redis的key值
     */
    toggleSpace(evt, ele, type, key) {

        evt.stopPropagation()
        if (type === "key") {
            return this.keyClick(key)
        }
        if (ele.parentNode.style.height === "22px") {
            ele.parentNode.style.height = "max-content"
        } else {
            ele.parentNode.style.height = "22px"
        }
        this.componentUpdate()
    }

    /**
     * 渲染命名空间
     * @param item {string | Object} 当前元素
     * @param index {number} 索引
     * @returns {JSX.Element|*}
     */
    renderNameSpace(item, index) {
        if (typeof item === "string") {
            return <div class={"key-item"} onClick={() => this.keyClick(item)}
                        key={index}><i
                class={"icon icon-key"}></i>
                {item}</div>
        }
        item.keyIndex = index
        let render = (obj) => {
            if (obj.keyIndex) {
                return <div
                    key={obj.keyIndex}
                    class={"space-item space-root"} >
                    <div class={"flag-line"}></div>
                    <div class={"flag-mask"} style={"height:100%"}></div>
                    <div onClick={(...args) => this.toggleSpace(...args, obj.type, obj.key)} class={"space-title"}>
                        {obj.type === "space" ? <i class="icon icon-folder"></i> : <i class={"icon icon-key"}></i>}
                        {obj.title}</div>
                    {obj.children ? render(obj.children) : ""}
                </div>
            }

            if (tools.valType(obj) === "Array") {
                return obj.map(obj => {
                    return <div
                        class={"space-item"}>
                        <div class={"flag-line"}></div>
                        <div class={"flag-mask"}></div>
                        <div onClick={(...args) => this.toggleSpace(...args, obj.type, obj.key)} class={"space-title"}>
                            {obj.type === "space" ? <i class="icon icon-folder"></i> : <i class={"icon icon-key"}></i>}
                            {obj.title}</div>
                        {obj.children ? render(obj.children) : ""}
                    </div>
                })
            }
            if (tools.valType(obj) === "Object") {
                return <div
                    class={"space-item"}>
                    <div class={"flag-line"}></div>
                    <div class={"flag-mask"}></div>
                    <div onClick={(...args) => this.toggleSpace(...args, obj.type, obj.key)} class={"space-title"}>
                        {obj.type === "space" ? <i class="icon icon-folder"></i> :
                            <i class={"icon icon-key"}></i>}
                        {obj.title}</div>
                    {obj.children ? render(obj.children) : ""}
                </div>
            }
        }
        return render(item)
    }

    /**
     * 渲染虚拟列表的每一行
     * @param item {string|Object} key或者命名空间对象
     * @param index {number} 索引
     * @returns {JSX.Element|*}
     */
    renderItem(item, index) {
        return this.renderNameSpace(item, index)
    }

    /**
     * 获取key列表
     * @param key {string} 搜索表达式
     */
    fetchKeyList(key) {
        setTimeout(() => {
            tools.xcall("async_get_keys", this.searchKey || "*", res => {
                if (res.code != 200) {
                    tools.alert(res.msg,{type:"error"})
                    return
                }
                let space = []
                let keys = []
                res.data.forEach(e => {
                    if (e.indexOf(":") > -1) {
                        space.push(e)
                    } else {
                        keys.push(e)
                    }
                })
                this.keyList = keys
                this.formatNamespace(space)
                this.componentUpdate()
            })
        })
    }

    /**
     * 根据key生成对应的命名空间数据
     * @param arr {array} keys列表
     */
    formatNamespace(arr) {
        let tmp = []
        let dg = (data, arrs, level, key) => {
            // 获取name
            let title = arrs.splice(0, 1)[0]
            // 获取是否存在root
            let root = data.find(e => e.title === title)
            // 如果arrs的长度为0，则到了最后一级
            if (arrs.length === 0) {
                //直接向data中push数据
                data.push(
                    {
                        title: title,
                        type: "key",
                        key,
                        level
                    }
                )
                return
            }
            // root存在
            if (root) {
                // 如果没有children创建children
                if (!root.children) root.children = []
                // 然后加入children
                return dg(root.children, arrs, level += 1, key)
            }
            // root 不存在代表是同级的，直接向data里面push数据
            root = {
                title: title,
                type: "space",
                children: [],
                level
            }
            data.push(root)
            // 继续往children递归
            dg(root.children, arrs, level += 1, key)
        }
        arr.forEach(e => {
            // 拆分命名空间
            let split = e.split(":")
            // 获取root的title名称
            let title = split[0]
            // 找一下tmp数组中是否已经存在root元素
            let root = tmp.find(e => e.title === title)
            // 如果不存在就插入一个根元素
            if (!root) {
                tmp.push({
                    title: title,
                    type: "space",
                    level: 0,
                    children: []
                })
            }
            // 递归生成数据
            dg(tmp, split, 0, e)
        })
        // 将数据赋给keyList数据
        this.keyList = this.keyList.concat(tmp)
    }

    render() {
        try {
            let list = []
            if (this.vlist) {
                let firstIndex = this.vlist.firstBufferIndex
                let lastIndex = this.vlist.lastBufferIndex
                let firstVisibleIndex = this.vlist.firstVisibleItem?.elementIndex || 0
                let lastVisibleIndex = this.vlist.lastVisibleItem?.elementIndex || lastIndex

                let totalItems = this.totalItems()

                if (firstVisibleIndex == 0)
                    this.post(() => {
                        this.vlist && this.vlist.navigate("start")
                    })
                else if (lastVisibleIndex >= totalItems) // number of items reduced so buffer is past total count
                    this.post(() => {
                        this.vlist && this.vlist.navigate("end")
                    })
                else if (this.vlist.itemsTotal != totalItems) { // number of items reduced, update scroll
                    lastIndex = firstIndex + Math.min(totalItems, this.vlist.slidingWindowSize) - 1
                    this.post(() => {
                        this.vlist.itemsAfter = totalItems - this.vlist.itemsBefore - this.children.length
                    })
                }
                for (let index = firstIndex; index <= lastIndex; ++index) {
                    let item = this.itemAt(index)
                    if (item) list.push(this.renderItem(item, index))
                }
            }
            return this.keyList.length > 0 ? <div class={"virtual-list"}>{list}</div> : <div class={"key-item-none"}>no keys</div>
            //return <div class={"virtual-list"}>{list}</div>
        } catch (e) {
            return <div class={"key-item-none"}>no keys</div>
        }
    }
}